package services

import (
	"context"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"short-link/internal/models"
	"time"
)

type URLService interface {
	CreateURL(c context.Context, dto models.UrlAddDto) (*models.Url, error)
	GetURL(c context.Context, shortCode string) (*models.Url, error)
	ClearURL(c context.Context, shortCode string) error
}

type URLServiceImpl struct {
	db        *gorm.DB
	generator ShortCodeGenerator
	duration  time.Duration
	cache     Cache
}

func NewURLServiceImpl(db *gorm.DB, generator ShortCodeGenerator, duration time.Duration, cache Cache) *URLServiceImpl {
	return &URLServiceImpl{
		db:        db,
		generator: generator,
		duration:  duration,
		cache:     cache,
	}
}

func (u *URLServiceImpl) CreateURL(c context.Context, dto models.UrlAddDto) (*models.Url, error) {
	var url = models.Url{}

	url.OriginalUrl = dto.OriginalURL
	if dto.CustomCode != "" { // 用户自定义
		available := u.shortCodeAvailable(dto.CustomCode)
		if !available {
			return nil, fmt.Errorf("custom code not available")
		}
		url.CustomCode = dto.CustomCode
		url.IsCustom = true
	} else { // 用户未自定义，系统自动生成
		code, err := u.getAvailableCode(0)
		if err != nil {
			return nil, err
		}
		url.CustomCode = code
	}

	if dto.Duration != nil {
		url.ExpiresAt = time.Now().Add(time.Duration(*dto.Duration) * time.Hour)
	} else {
		url.ExpiresAt = time.Now().Add(u.duration)
	}

	res := u.db.Create(&url)
	if res.Error != nil {
		return nil, res.Error
	}

	err := u.cache.SetURL(c, url)
	if err != nil {
		return nil, err
	}

	return &url, nil
}

func (u *URLServiceImpl) GetURL(c context.Context, shortCode string) (*models.Url, error) {
	// 1.先去缓存查询是否命中
	vo, err := u.cache.GetURL(c, shortCode)
	if err != nil {
		return nil, err
	}
	if vo != nil {
		return vo, err
	}

	// 2 查询数据库
	var url = models.Url{}
	res := u.db.First(&url, "custom_code = ? and expires_at > ?", shortCode, time.Now())
	if res.Error != nil {
		return nil, res.Error
	}

	// 3 缓存
	err1 := u.cache.SetURL(c, url)
	if err1 != nil {
		return nil, err1
	}

	return &url, nil
}

func (u *URLServiceImpl) ClearURL(c context.Context, shortCode string) error {
	var url = models.Url{}
	res := u.db.Delete(&url, "custom_code = ?", shortCode)
	if res.Error != nil {
		return res.Error
	}
	return nil
}

// 验证 CustomCode 是否以及已经存在
func (u *URLServiceImpl) shortCodeAvailable(shortCode string) bool {
	var url models.Url
	res := u.db.Where("custom_code = ?", shortCode).First(&url)
	if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
		return true // 没有找到，表示该shortCode可用
	}
	return false // 找到记录，表示shortCode不可用

}

// 随机生成一个可用的 customCode
func (u *URLServiceImpl) getAvailableCode(n int) (string, error) {
	if n >= 5 {
		return "", errors.New("try 5 times and failed")
	}
	shortCode := u.generator.NextID()

	isAvailable := u.shortCodeAvailable(shortCode)

	if !isAvailable {
		return u.getAvailableCode(n + 1)
	}
	return shortCode, nil
}
